package com.szyibei.system.gateway.filter;

import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.SslInfo;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;

import com.alibaba.fastjson.JSON;
import com.szyibei.system.common.common.CommonConstant;
import com.szyibei.system.common.exception.BizRuntimeException;
import com.szyibei.system.common.utils.LoggerUtil;
import com.szyibei.system.common.utils.RequestUtil;
import com.szyibei.system.common.vo.RequestBaseVO;
import com.szyibei.system.common.vo.ResponseBaseVO;
import com.szyibei.system.gateway.entity.ErrorResponse;

import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Configuration
@Slf4j
public class AccessGatewayFilter implements GlobalFilter {
//    private static final Logger logger = Logger.getLogger(AccessGatewayFilter.class);

//    private static final String GATEWAY="gateway";
    /**
     * 
     * 路由全局过滤器
     * 
     * @param serverWebExchange
     * @param gatewayFilterChain
     * @return
     * @see org.springframework.cloud.gateway.filter.GlobalFilter#filter(org.springframework.web.server.ServerWebExchange, org.springframework.cloud.gateway.filter.GatewayFilterChain)
     */

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain) {
        LoggerUtil.info(log, "gateway-------globalfilter------");
        ServerHttpRequest request = serverWebExchange.getRequest();
        URI originUri = request.getURI();
        LoggerUtil.info(log, "全局过滤器--originUri:{}", originUri);
        try {
            String serviceCode = request.getQueryParams().getFirst("service_code");
            LoggerUtil.info(log, "全局过滤器--serviceCode:{}", serviceCode);
            String method = request.getMethodValue();
//            checkPath(originUri);
            switch (method) {
                case "GET":
                    return getChainFilter(serverWebExchange, gatewayFilterChain, request);
                default:
                    return defaultChainFilter(serverWebExchange, gatewayFilterChain, request);
            }

        } catch (Exception e) {
            LoggerUtil.error(e, log, "出错了:{}", e.getMessage());
            return getVoidMono(serverWebExchange, new ErrorResponse(e.getMessage()));
        }

    }

//    private static void checkPath(URI uri) {
//        String path = uri.getPath();
//        String[] sp = path.split("/");
//        List<String> asList = Arrays.asList(sp);
//        if (asList.size() <= 1 || (asList.size() == 2 && !GATEWAY.equals(asList.get(1)))) {
//            throw new BizRuntimeException("请求地址出错!");
//        }
//    }

    private Mono<Void> getChainFilter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain, ServerHttpRequest request)
            throws Exception {
        URI uri = request.getURI();
        if ("/gateway".equals(uri.getPath())) {
            String serviceCode = request.getQueryParams().getFirst("service_code");
            String nacosServicePath = vaildServiceCode(serviceCode);
            uri = buildUri(nacosServicePath, uri);
            LoggerUtil.info(log, "全局过滤器--转发地址:{}", uri);
            serverWebExchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri);
        } else {
            LoggerUtil.info(log, "全局过滤器--跳转地址:{}", uri);
            ServerHttpRequest.Builder mutate = request.mutate();
            ServerHttpRequest build = mutate.build();
            return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());
        }

        //下面将请求体再次封装写回到 request 里,传到下游服务.
        URI ex = UriComponentsBuilder.fromUri(uri).build(true).toUri();
        ServerHttpRequest newRequest = request.mutate().uri(ex).build();
        newRequest = new ServerHttpRequestDecorator(newRequest) {
            @Override
            @Nullable
            public HttpMethod getMethod() {
                return request.getMethod();
            }

            @Override
            public String getMethodValue() {
                return request.getMethodValue();
            }

            @Override
            public URI getURI() {
                return request.getURI();
            }

            @Override
            public RequestPath getPath() {
                return request.getPath();
            }

            @Override
            public MultiValueMap<String, String> getQueryParams() {
                return request.getQueryParams();
            }

            @Override
            public HttpHeaders getHeaders() {
                return request.getHeaders();
            }

            @Override
            public MultiValueMap<String, HttpCookie> getCookies() {
                return request.getCookies();
            }

            @Override
            public InetSocketAddress getRemoteAddress() {
                return request.getRemoteAddress();
            }

            @Nullable
            @Override
            public SslInfo getSslInfo() {
                return request.getSslInfo();
            }

            @Override
            public Flux<DataBuffer> getBody() {
                return request.getBody();
            }
        };
        ServerWebExchange newExchange = serverWebExchange.mutate().request(newRequest).build();
        return gatewayFilterChain.filter(newExchange);
    }

    private Mono<Void> defaultChainFilter(ServerWebExchange serverWebExchange, GatewayFilterChain gatewayFilterChain, ServerHttpRequest request)
            throws Exception {
        URI uri = request.getURI();
        RequestBaseVO vo = null;
        String bodyStr = null;
        if ("/gateway".equals(uri.getPath())) {
            String contentType = request.getHeaders().getFirst("Content-Type");
            bodyStr = resolveBodyFromRequest(request);
            if (CommonConstant.CONTENTTYPE_JSON.equalsIgnoreCase(contentType)) {
                vo = JSON.parseObject(bodyStr, RequestBaseVO.class);
            } else if (CommonConstant.CONTENTTYPE_X_WWW_FORM_URLENCORDED.equalsIgnoreCase(contentType)) {
                Map<String, Object> map = RequestUtil.transStringToMap(bodyStr, "&", "=");
                vo = JSON.parseObject(JSON.toJSONString(map), RequestBaseVO.class);
            }
            LoggerUtil.info(log, "全局过滤器--RequestBaseVO:{}", vo);
            String nacosServicePath = vaildPath(vo);
            uri = buildUri(nacosServicePath, uri);
            LoggerUtil.info(log, "全局过滤器--转发地址::{}", uri);
            serverWebExchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri);
        } else {
            LoggerUtil.info(log, "全局过滤器--跳转地址::{}", uri);
            ServerHttpRequest.Builder mutate = request.mutate();
            ServerHttpRequest build = mutate.build();
            return gatewayFilterChain.filter(serverWebExchange.mutate().request(build).build());
        }

        //下面将请求体再次封装写回到 request 里,传到下游服务.
        URI ex = UriComponentsBuilder.fromUri(uri).build(true).toUri();
        ServerHttpRequest newRequest = request.mutate().uri(ex).build();
        DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
        Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
        newRequest = new ServerHttpRequestDecorator(newRequest) {
            @Override
            public Flux<DataBuffer> getBody() {
                return bodyFlux;
            }

            @Override
            @Nullable
            public HttpMethod getMethod() {
                return request.getMethod();
            }

            @Override
            public String getMethodValue() {
                return request.getMethodValue();
            }

            @Override
            public URI getURI() {
                return request.getURI();
            }

            @Override
            public RequestPath getPath() {
                return request.getPath();
            }

            @Override
            public MultiValueMap<String, String> getQueryParams() {
                return request.getQueryParams();
            }

            @Override
            public HttpHeaders getHeaders() {
                return request.getHeaders();
            }

            @Override
            public MultiValueMap<String, HttpCookie> getCookies() {
                return request.getCookies();
            }

            @Override
            public InetSocketAddress getRemoteAddress() {
                return request.getRemoteAddress();
            }

            @Nullable
            @Override
            public SslInfo getSslInfo() {
                return request.getSslInfo();
            }
        };
        ServerWebExchange newExchange = serverWebExchange.mutate().request(newRequest).build();
        return gatewayFilterChain.filter(newExchange);
    }

    /**
     * 重新封装uri
     */
    private URI buildUri(String nacosServicePath, URI uri) {
        String queryString = uri.getQuery();
        StringBuilder sb = new StringBuilder();
        sb.append(uri.getScheme());
        sb.append("://");
        sb.append(uri.getHost());
        sb.append(":");
        sb.append(uri.getPort());
        sb.append("/");
        sb.append(nacosServicePath);
        if (StringUtils.isNotBlank(queryString)) {
            sb.append("?");
            sb.append(queryString);
        }
        URI uriNew = UriComponentsBuilder.fromHttpUrl(sb.toString()).build().toUri();
        return uriNew;
    }

    private String vaildPath(RequestBaseVO vo) throws BizRuntimeException {
        if (vo == null || vo.getServiceCode() == null) {
            throw new BizRuntimeException("请求服务码缺失!");
        }
        String path = vo.getServiceCode().replace(".", "/");
//        String newPath = "/" + Arrays.stream(org.springframework.util.StringUtils.tokenizeToStringArray(path, "/"))
//                .skip(4).collect(Collectors.joining("/"));
        return path;
        //        return ServiceCodeEunms.getNacosServicePath(vo.getServiceCode());
    }

    private String vaildServiceCode(String serviceCode) throws BizRuntimeException {
        if (serviceCode == null) {
            throw new BizRuntimeException("请求服务码缺失!");
        }
        return serviceCode.replace(".", "/");
        //        return ServiceCodeEunms.getNacosServicePath(vo.getServiceCode());
    }

    /**
     * 去掉空格,换行和制表符
     * @param str
     * @return
     */
    //    private String formatStr(String str) {
    //        if (str != null && str.length() > 0) {
    //            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
    //            Matcher m = p.matcher(str);
    //            return m.replaceAll("");
    //        }
    //        return str;
    //    }

    private DataBuffer stringBuffer(String value) {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }

    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) throws InterruptedException {
        String method = serverHttpRequest.getMethodValue();
        String contentType = serverHttpRequest.getHeaders().getFirst("Content-Type");
        LoggerUtil.info(log, "全局过滤器--resolveBodyFromRequest:method:{}---contentType:{}", method, contentType);

        Flux<DataBuffer> body = serverHttpRequest.getBody();

        StringBuilder sb = new StringBuilder();
        body.subscribe(buffer -> {
            byte[] bytes = new byte[buffer.readableByteCount()];
            buffer.read(bytes);
            DataBufferUtils.release(buffer);
            String bodyString;
            try {
                bodyString = new String(bytes, "utf-8");
                LoggerUtil.info(log, "全局过滤器--resolveBodyFromRequest:bodyString:{}", bodyString);
                sb.append(bodyString);
            } catch (UnsupportedEncodingException e) {
                LoggerUtil.error(e, log, "全局过滤器--resolveBodyFromRequest:异常:{}", e);
            }

        });
        String reqBody = sb.toString();
        return reqBody;

    }

    /**
     * 网关抛异常
     *
     * @param body
     */
    private Mono<Void> getVoidMono(ServerWebExchange serverWebExchange, ResponseBaseVO body) {
        serverWebExchange.getResponse().setStatusCode(HttpStatus.OK);
        byte[] bytes = JSON.toJSONString(body).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverWebExchange.getResponse().bufferFactory().wrap(bytes);
        return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
    }
}